#include "pwext.hpp"
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/signal.h>
#include <list>

struct Request
{
	bool isValid()
	{
		return false;
	}
	void handle()
	{ }
};
Request get_next_request()
{
	return Request();
}

pw::semaphore started(0);


// [[ex05bd]]
// DEBUG descr The repeated signaling to multiple threads in parallel.
class worker : public pw::pwthread
{
public:
	worker() : 
		exit_flag_(false), exit_confirm_flag_(false)
	{ }
	// ...
	void *execute()
	{
		while(!get_exit_flag()) {
	// DEBUG {
			fprintf(stderr, "worker thread %p in the loop\n", this);
			started.signal();
			pw::event delay;
			delay.msecwait(10); // artificially increase the race window
			fprintf(stderr, "worker thread %p is about to read the request\n", this);
	// DEBUG }
			Request r = get_next_request();

			if (get_exit_flag())
				break;
			if (!r.isValid())
				continue;

			// ... handle the request until need to sleep...
			if (mysleep(1000) != ETIMEDOUT)
				break; // it means, the exit was signaled
			// ... continue handling the request
		}
		{
			pw::lockmutex lm(exit_mutex_);
			exit_confirm_flag_ = true;
		}
		exit_confirm_event_.signal();
	// DEBUG {
		fprintf(stderr, "worker thread %p exits\n", this);
	// DEBUG }
	}

	void stop()
	{
		{
			pw::lockmutex lm(exit_mutex_);
			if (exit_confirm_flag_) // already stopped
				return;
			exit_flag_ = true;
		}
		exit_event_.signal();
		if (id_ != 0)
			// id_ is inherited from pw::thread
			pthread_kill(id_, SIGUSR1); 
	}

	void stop_and_confirm()
	{
		{
			pw::lockmutex lm(exit_mutex_);
			exit_flag_ = true;
		}
		exit_event_.signal();
		do {
			if (id_ != 0)
				// id_ is inherited from pw::thread
				pthread_kill(id_, SIGUSR1); 
			else
				break;
		} while(exit_confirm_event_.msecwait(1) 
				== ETIMEDOUT);
	}

	bool has_exited()
	{
		pw::lockmutex lm(exit_mutex_);
		return exit_confirm_flag_;
	}

	// will return 0 if exit was signaled, ETIMEDOUT
	// otherwise
	int mysleep(int milliseconds)
	{
		return exit_event_.msecwait(milliseconds);
	}

protected:
	bool get_exit_flag()
	{
		pw::lockmutex lm(exit_mutex_);
		return exit_flag_;
	}

	bool exit_flag_;
	bool exit_confirm_flag_;
	pw::pmutex exit_mutex_;
	pw::event exit_event_;
	pw::event exit_confirm_event_;
	// ...
};

// DEBUG {
void dummyhandler(int sig)
{ }

int main(int argc, char **argv)
{
	// ...
	struct sigaction sa;
	sa.sa_handler = dummyhandler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0; // do not specify SA_RESTART !!!
	sigaction(SIGUSR1, &sa, NULL);
// DEBUG }
// ... in the main/stopper thread
std::list<worker *> workers;
std::list<worker *>::iterator it;
// ...
// DEBUG {
	const int NWORKERS = 3;
	for (int i = 0; i < NWORKERS; i++) {
		worker *t = new worker;
		workers.push_back(t);
	}
	for (it = workers.begin(); it != workers.end(); ++it)
		(*it)->start();
	started.wait(NWORKERS);
// DEBUG }
// stopping:
while (!workers.empty())
{
	// DEBUG {
	fprintf(stderr, "a stop attempt\n");
	// DEBUG }
	for (it = workers.begin(); it != workers.end(); ++it)
		(*it)->stop();
	for (it = workers.begin(); it != workers.end(); ) {
		if ((*it)->has_exited()) {
	// DEBUG {
			fprintf(stderr, "detected %p exited\n", (*it));
	// DEBUG }
			(*it)->join();
			delete (*it);
			workers.erase(it++);
		} else {
			++it;
		}
	}
	if (!workers.empty())
		usleep(1000); // 1000 mcs = 1 ms
}
// END
// DEBUG {
}
// DEBUG }

/* Sample output (timing may vary):
worker thread 0x1415010 in the loop
worker thread 0x1415190 in the loop
worker thread 0x1415310 in the loop
a stop attempt
a stop attempt
a stop attempt
a stop attempt
a stop attempt
a stop attempt
a stop attempt
a stop attempt
worker thread 0x1415010 is about to read the request
worker thread 0x1415010 exits
worker thread 0x1415190 is about to read the request
worker thread 0x1415190 exits
worker thread 0x1415310 is about to read the request
worker thread 0x1415310 exits
a stop attempt
detected 0x1415010 exited
detected 0x1415190 exited
detected 0x1415310 exited
*/
